#pragma once
#include <iostream>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#include "inetAddr.hpp"
#include "log.hpp"
#include "common.hpp"

using namespace LogModule;

const static int backlog = 8;

class TcpServer : public Nocopy
{
public:
    TcpServer(InetAddr addr)
        : _addr(addr), _isRuning(false)
    {
    }

    void Init()
    {
        // 1、创建套接字
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG((int)LogLevel::FATAL) << "套接字创建失败!";
            exit(SOCKET_ERR);
        }
        LOG((int)LogLevel::INFO) << "套接字创建成功！";
        // 2、绑定端口，ip
        int n = bind(_sockfd, _addr.Sockaddr(), sizeof(sockaddr_in));
        if (n < 0)
        {
            LOG((int)LogLevel::FATAL) << "端口绑定失败!";
            exit(BIND_ERR);
        }
        LOG((int)LogLevel::INFO) << "端口绑定成功！";
        // 3、进行聆听
        int m = listen(_sockfd, backlog);
        if (m < 0)
        {
            LOG((int)LogLevel::FATAL) << "聆听失败!";
            exit(LISTEN_ERR);
        }
        LOG((int)LogLevel::INFO) << "聆听成功！";
    }

    void Service(int sockfd, InetAddr &peer)
    {
        char buffer[1024];
        while (true)
        {
            // 1. 先读取数据
            // a. n>0: 读取成功
            // b. n<0: 读取失败
            // c. n==0: 对端把链接关闭了，读到了文件的结尾 --- pipe
            ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                // buffer是一个英文单词 or 是一个命令字符串
                buffer[n] = 0; // 设置为C风格字符串， n<= sizeof(buffer)-1
                LOG((int)LogLevel::DEBUG) << peer.AddrMessage() << " #" << buffer;

                // std::string echo_string = _func(buffer, peer);

                // 2. 写回数据
                std::string echo_string = buffer;

                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                LOG((int)LogLevel::DEBUG) << peer.AddrMessage() << " 退出了...";
                close(sockfd);
                break;
            }
            else
            {
                LOG((int)LogLevel::DEBUG) << peer.AddrMessage() << " 异常...";
                close(sockfd);
                break;
            }
        }
    }

    void Start()
    {
        _isRuning = true;
        while (_isRuning)
        {
            sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int fd = accept(_sockfd, CONV(&peer), &len);
            if (fd < 0)
            {
                LOG((int)LogLevel::FATAL) << "连接失败！";
                exit(ACCEPT_ERR);
            }
            LOG((int)LogLevel::INFO) << "连接成功！";
            InetAddr addr(peer);

            // 1、版本一：不存在这样的真实情况
            //  Service(fd, addr);

            // 2、版本二：通过让孤儿进程去执行service方法
            // pid_t pid = fork();
            // if (pid == 0)
            // {
            //     // 子进程
            //     close(_sockfd);
            //     if (fork() > 0)
            //     {
            //         exit(OK);
            //     }
            //     Service(fd, addr);
            // }
            // else
            // {
            //     close(fd);
            //     pid_t n = waitpid(pid, nullptr, 0);
            //     if(n < 0)
            //     {
            //         LOG((int)LogLevel::FATAL) << "进程等待失败！";
            //         exit(PRO_ERR);
            //     }
            // }

            //3、版本三：通过创建线程来执行任务
            pthread_t tid;
            ThreadTransfer* TTransfer = new ThreadTransfer(fd, peer, this);
            pthread_create(&tid, nullptr, routine, TTransfer);
        }
    }

    class ThreadTransfer
    {
    public:
        ThreadTransfer(int fd, InetAddr addr, TcpServer* tser)
            :_fd(fd)
            ,_addr(addr)
            ,_tser(tser)
        {}

    public:
        int _fd;
        InetAddr _addr;
        TcpServer *_tser;
    };

    static void* routine(void *ptr)
    {
        pthread_detach(pthread_self());
        ThreadTransfer* TTransfer = static_cast<ThreadTransfer*>(ptr);
        TTransfer->_tser->Service(TTransfer->_fd, TTransfer->_addr);
        delete(TTransfer);
        return nullptr;
    }

    ~TcpServer()
    {
    }

private:
    int _sockfd;
    InetAddr _addr;
    bool _isRuning;
};